﻿using System;
using System.Collections.Generic;
using System.IO;

namespace Hive.Core.Model
{
    /// <summary>
    /// A default hive manager.
    /// </summary>
    public class DefaultHiveManager : IHiveManager
    {
        private const string HiveConfigurationFileName = ".hive";

        /// <inheritdoc />
        public DirectoryInfo FindHiveDirectory(DirectoryInfo currentDirectory)
        {
            if (currentDirectory == null)
                throw new ArgumentNullException("currentDirectory");

            while (currentDirectory != null)
            {
                if (HasHiveConfiguration(currentDirectory))
                    return currentDirectory;

                currentDirectory = currentDirectory.Parent;
            }

            return null;
        }

        /// <inheritdoc />
        public bool Exists(DirectoryInfo directory)
        {
            if (directory == null)
                throw new ArgumentNullException("directory");

            return HasHiveConfiguration(directory);
        }

        /// <inheritdoc />
        public IHive Open(DirectoryInfo directory)
        {
            if (directory == null)
                throw new ArgumentNullException("directory");

            if (!HasHiveConfiguration(directory))
                throw new HiveException(string.Format("The directory '{0}' does not exist or is not a recognized hive.", directory.FullName));

            HiveConfiguration configuration = LoadHiveConfiguration(directory);

            return OpenWithConfiguration(directory, configuration);
        }

        /// <inheritdoc />
        public IHive Create(DirectoryInfo directory, HiveConfiguration configuration)
        {
            if (directory == null)
                throw new ArgumentNullException("directory");
            if (configuration == null)
                throw new ArgumentNullException("configuration");

            if (HasHiveConfiguration(directory))
                throw new HiveException(string.Format("The directory '{0}' already contains a hive.", directory.FullName));

            if (directory.Exists)
                throw new HiveException(string.Format("The directory '{0}' must not exist in order to create a new hive there.", directory.FullName));

            SaveHiveConfiguration(directory, configuration);

            return OpenWithConfiguration(directory, configuration);
        }

        /// <inheritdoc />
        public void Reconfigure(DirectoryInfo directory, HiveConfiguration configuration)
        {
            if (directory == null)
                throw new ArgumentNullException("directory");
            if (configuration == null)
                throw new ArgumentNullException("configuration");

            if (!HasHiveConfiguration(directory))
                throw new HiveException(string.Format("The directory '{0}' does not exist or is not a recognized hive.", directory.FullName));

            SaveHiveConfiguration(directory, configuration);
        }

        private static IHive OpenWithConfiguration(DirectoryInfo directory, HiveConfiguration configuration)
        {
            ISourceRepository sourceRepository = configuration.SourceRepositoryConfiguration.CreateSourceRepository();
            IBinaryRepository binaryRepository = configuration.BinaryRepositoryConfiguration.CreateBinaryRepository();
            return new DefaultHive(directory, configuration, sourceRepository, binaryRepository);
        }

        private static bool HasHiveConfiguration(DirectoryInfo directory)
        {
            return GetHiveConfigurationFile(directory).Exists;
        }

        private static HiveConfiguration LoadHiveConfiguration(DirectoryInfo directory)
        {
            FileInfo file = GetHiveConfigurationFile(directory);
            Descriptor descriptor = Descriptor.Load(file);
            return new HiveConfiguration(descriptor);
        }

        private static void SaveHiveConfiguration(DirectoryInfo directory, HiveConfiguration configuration)
        {
            FileInfo file = GetHiveConfigurationFile(directory);
            Descriptor descriptor = configuration.ToDescriptor();

            if (! file.Directory.Exists)
                file.Directory.Create();

            descriptor.Save(file);
        }

        private static FileInfo GetHiveConfigurationFile(DirectoryInfo directory)
        {
            return new FileInfo(Path.Combine(directory.FullName, HiveConfigurationFileName));
        }
    }
}
